package com.shark.uh.service.impl;

import com.shark.uh.enums.ResponseCode;
import com.shark.uh.exception.BusinessException;
import com.shark.uh.model.dto.captcha.ImageCaptchaDTO;
import com.shark.uh.service.ImageCaptchaService;
import com.shark.uh.service.RedisService;
import com.shark.uh.utils.Generator;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.*;

@Log4j2
@Service
public class ImageCaptchaServiceImpl implements ImageCaptchaService {

    @Resource
    private RedisService redisService;

    /**
     * 创建图形验证码 (0-9，a-z,A-Z)。
     * <p>
     * 示例值：AB12cd
     * </p>
     *
     * @return 图形验证码数据传输对象。
     */
    @Override
    public ImageCaptchaDTO create() {
        // 生成文字验证码
        String verificationCode = Generator.generateCaptcha();

        // 使用文字验证码生成base64编码的图片
        String base64ImageCaptcha = this.createBase64ImageCaptcha(verificationCode);

        // 生成验证码ID（UUID）
        String verificationId = UUID.randomUUID().toString().replace("-", "");

        // 将验证码存入Redis
        redisService.saveToRedisMinute(verificationId, verificationCode, 10);

        // 创建ImageCaptchaDTO对象并返回.
        ImageCaptchaDTO imageCaptchaDTO = new ImageCaptchaDTO();
        imageCaptchaDTO.setVerificationId(verificationId);
        imageCaptchaDTO.setBase64EncodedImage(base64ImageCaptcha);
        return imageCaptchaDTO;
    }

    /**
     * 验证图形验证码。
     *
     * @param verificationId   验证码ID
     * @param verificationCode 验证码
     */
    @Override
    public void verify(String verificationId, String verificationCode) {

        // 从redis中get验证码
        String verificationCodeCache = redisService.getString(verificationId);

        // 验证码过期
        if (verificationCodeCache == null) {
            throw new BusinessException(ResponseCode.CAPTCHA_EXPIRED);
        }

        // 验证码错误
        if (!verificationCodeCache.equals(verificationCode)) {
            throw new BusinessException(ResponseCode.CAPTCHA_WRONG);
        }

        // 验证成功删除验证码
        redisService.del(verificationId);
    }


    private String createBase64ImageCaptcha(String captchaText) {
        // 验证码图片宽度和高度
        int width = 160;
        int height = 60;

        // 创建 BufferedImage 对象
        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = bufferedImage.createGraphics();

        // 设置背景颜色
        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, width, height);

        // 绘制干扰线
        g2d.setColor(Color.LIGHT_GRAY);
        for (int i = 0; i < 10; i++) {
            int x1 = (int) (Math.random() * width);
            int y1 = (int) (Math.random() * height);
            int x2 = (int) (Math.random() * width);
            int y2 = (int) (Math.random() * height);
            g2d.drawLine(x1, y1, x2, y2);
        }

        // 随机字体大小和字体
        String[] fonts = {"Arial", "Courier New", "Georgia", "Times New Roman", "Verdana"};
        Random random = new Random();
        int fontSize = random.nextInt(15) + 30; // 字体大小在 30 到 45 之间
        String fontName = fonts[random.nextInt(fonts.length)];
        g2d.setFont(new Font(fontName, Font.BOLD, fontSize));

        // 绘制验证码字符
        FontMetrics fontMetrics = g2d.getFontMetrics();
        int charX = (width - fontMetrics.stringWidth(captchaText)) / 2;
        int charY = (height - fontMetrics.getHeight()) / 2 + fontMetrics.getAscent();

        // 设置随机颜色（非白色）
        do {
            g2d.setColor(new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256)));
        } while (g2d.getColor().equals(Color.WHITE));
        g2d.drawString(captchaText, charX, charY);

        // 释放 Graphics2D 对象
        g2d.dispose();

        // 将图片转换为 Base64 编码字符串
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            ImageIO.write(bufferedImage, "png", outputStream);
            byte[] imageBytes = outputStream.toByteArray();
            return Base64.getEncoder().encodeToString(imageBytes);
        } catch (Exception e) {
            return null;
        }
    }

}
